package iharbor2

import (
	"bytes"
	"context"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/rclone/rclone/backend/iharbor2/api"
	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/lib/rest"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/url"
	"regexp"
	"strconv"
)

// 正则匹配 查看创建的存储桶数量是否达到上限
func reqTheMaximumNumberOfBucketsHasBeenReached(code_text string) (b bool) {
	// 目前仅使用部分权限
	m, _ := regexp.MatchString("存储桶数量已达上限", code_text)

	return m

}

// 检查桶是否创建
func (f *Fs) doesTheBucketExist(ctx context.Context, bucket string) (b bool, err error) {
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/buckets/" + bucket + "/"
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, url.Values{"by-name": {"true"}},
		nil, nil, nil)

	//opts = rest.Opts{
	//	Method:     "GET",
	//	Path:       "/buckets/" + bucket + "/",
	//	Parameters: url.Values{"by-name": {"true"}},
	//}

	var response api.Error
	resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
	if err != nil {

		//return false, errors.New("the bucket does not exist, " +
		//	"please go to the https://service.cstcloud.cn to create the bucket")
		return false, err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 404 {
		// 桶不存在
		//{
		//	"code": "NoSuchBucket",
		//	"code_text": "存储桶不存在"
		//}
		return false, nil
	}
	if resp.StatusCode == 200 {
		// 桶存在
		return true, nil
	}

	return false, err
}

// 目录是否存在
func (f *Fs) doesTheDirectoryExist(ctx context.Context, bucket, bucketPath string) (b bool, err error) {
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/" + bucketPath + "/"
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, url.Values{"by-name": {"true"}},
		nil, nil, nil)

	//opts = rest.Opts{
	//	Method:     "GET",
	//	Path:       "/dir/" + bucket + "/" + bucketPath + "/",
	//	Parameters: url.Values{"by-name": {"true"}},
	//}

	var response api.Error
	resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
	if resp.StatusCode == 404 {
		// 目录不存在
		//{
		//	"code": "NoParentPath",
		//	"code_text": "目录路径不存在"
		//}
		return false, nil
	}
	if resp.StatusCode == 200 {
		// 目录存在
		return true, nil
	}
	if err != nil {
		//return false, errors.New("Directory does not exist.")
		return false, err
	}
	defer resp.Body.Close()

	return false, nil
}

// 创建一个目录
func (f *Fs) createDirectory(ctx context.Context, bucket, bucketPath string) (b bool, err error) {
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/" + bucketPath + "/"
	opts = f.HandlerAccesskeyHeader("POST", Path, 3600, nil, nil,
		nil, nil)

	//opts := rest.Opts{
	//	Method: "POST",
	//	Path:   "/dir/" + bucket + "/" + bucketPath + "/",
	//}
	var response api.Error
LOOP:
	resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
	if resp == nil {
		goto LOOP
	}
	if resp.StatusCode == 201 {
		// 目录创建成功
		return true, nil
	}
	if err != nil {
		return false, err
	}
	defer resp.Body.Close()

	return false, nil

}

func (f *Fs) traverseDirectory1(ctx context.Context, bucket, directory, basePath string) error {
	var opts rest.Opts

	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/"
	if directory != "" {
		Path = "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"
	}

	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, nil, nil,
		nil, nil)

	// directory 为目录
	//opts := rest.Opts{
	//	Method:  "GET",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//	Path:    bucket + "/" + directory + "/",
	//}
	//if directory == "" {
	//	opts = rest.Opts{
	//		Method:  "GET",
	//		RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//		Path:    bucket + "/",
	//	}
	//}

	var response api.TraverseDirectory
	err := f.pacer.Call(func() (bool, error) {
		resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
		return f.shouldRetry(ctx, resp, err)
	})
	if err != nil {
		matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
		if matchString == true {
			return nil
		}
		return err
	}
	// count == 0  该目录为空目录

	// 查看该目录下的 返回值 count 如果为 0  代表该目录下没有文件
	// 如果count 不为 0 的时候 需要遍历 files中的内容
	if response.Count == 0 {
		err := f.deleteMune(ctx, bucket, directory)
		if err != nil {
			return err
		}
		//// 目录删除完后 要递归删除
		err = f.RegexpDir(ctx, bucket, directory, basePath)
		if err != nil {
			return err
		}

	} else {
		for i := range response.Files {
			// 遍历Files中的目录和文件
			k := &response.Files[i]

			if k.IsFileOrDir == true {
				err := f.deleteFile(ctx, bucket, k.AllFilePath, basePath)
				if err != nil {
					return err
				}
			} else {
				//fmt.Printf("遍历目录 = %s \n", k.AllFilePath)
				err := f.traverseDirectory(ctx, bucket, k.AllFilePath, basePath)
				if err != nil {
					return err
				}
			}
		}

	}
	return nil
}

func (f *Fs) traverseDirectoryTest(ctx context.Context, bucket, directory, basePath string) error {

	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/"
	if directory != "" {
		Path = "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"
	}
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, nil, nil,
		nil, nil)

	var response api.TraverseDirectory

LOOP:
	resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
	if resp == nil {
		goto LOOP
	}
	if resp.StatusCode == 404 {
		matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
		if matchString == true {
			return nil
		}
	}

	//if err != nil && err != io.EOF {
	//	//matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
	//	//if matchString == true {
	//	//	return nil
	//	//}
	//	if resp.StatusCode == 404 {
	//		matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
	//		if matchString == true {
	//			return nil
	//		}
	//	}
	//	return fmt.Errorf("请求目录文件时出现错误：%v", err.Error())
	//}
	if resp.StatusCode != 200 {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("解析body时出现错误：%v ", err)
		}
		return fmt.Errorf("获取目录文件时错误：%v", body)
	}
	if response.Count == 0 {
		err := f.deleteMune(ctx, bucket, directory)
		if err != nil {
			return err
		}
		//// 目录删除完后 要递归删除
		//err = f.RegexpDir(ctx, bucket, directory, basePath)
		//if err != nil {
		//	return err
		//}

	} else {

		return errors.New("继续删除")
	}
	//defer resp.Body.Close()
	return nil
}

func (f *Fs) traverseDirectory(ctx context.Context, bucket, directory, basePath string) error {

	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/"
	if directory != "" {
		Path = "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"
	}
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, nil, nil,
		nil, nil)

	//// directory 为目录
	//opts := rest.Opts{
	//	Method:  "GET",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//	Path:    bucket + "/" + directory + "/",
	//}
	//if directory == "" {
	//	opts = rest.Opts{
	//		Method:  "GET",
	//		RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//		Path:    bucket + "/",
	//	}
	//}

	var response api.TraverseDirectory
	resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
	defer resp.Body.Close()
	if err != nil && err != io.EOF {
		//matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
		//if matchString == true {
		//	return nil
		//}
		if resp.StatusCode == 404 {
			matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
			if matchString == true {
				return nil
			}
		}
		return fmt.Errorf("请求目录文件时出现错误：%v", err.Error())
	}
	if resp.StatusCode != 200 {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("解析body时出现错误：%v ", err)
		}
		return fmt.Errorf("获取目录文件时错误：%v", body)
	}
	if response.Count == 0 {
		err := f.deleteMune(ctx, bucket, directory)
		if err != nil {
			return err
		}
		//// 目录删除完后 要递归删除
		err = f.RegexpDir(ctx, bucket, directory, basePath)
		if err != nil {
			return err
		}

	} else {
		for i := range response.Files {
			// 遍历Files中的目录和文件
			k := &response.Files[i]

			if k.IsFileOrDir == true {
				err := f.deleteFile(ctx, bucket, k.AllFilePath, basePath)
				if err != nil {
					return err
				}
			} else {
				//fmt.Printf("遍历目录 = %s \n", k.AllFilePath)
				err := f.traverseDirectory(ctx, bucket, k.AllFilePath, basePath)
				if err != nil {
					return err
				}
			}
		}

	}
	return nil
}

// 删除文件
func (f *Fs) deleteFile(ctx context.Context, bucket, bucketPath, basePath string) error {
	fmt.Printf("删除文件 %s \n", bucketPath)
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/obj/" + bucket + "/" + bucketPath + "/"
	opts = f.HandlerAccesskeyHeader("DELETE", Path, 3600, nil, nil,
		nil, nil)
	//opts.NoResponse = true
	//opts := rest.Opts{
	//	Method:  "DELETE",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/obj/",
	//	Path:    bucket + "/" + bucketPath + "/",
	//}
	//var response api.Error

	err := f.pacer.Call(func() (bool, error) {
	LOOP:
		resp, err := f.srv.Call(ctx, &opts)
		if resp == nil {
			goto LOOP
		}

		return f.shouldRetry(ctx, resp, err)
	})
	if err != nil {
		return fmt.Errorf("failed to delete file: %w", err)
	}
	//LOOP:
	//	resp, err := f.srv.Call(ctx, &opts)
	//	if resp == nil {
	//
	//		goto LOOP
	//	} else {
	//		if resp.StatusCode == 404 {
	//			matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
	//			if matchString == true {
	//				return nil
	//			}
	//			matchString, _ = regexp.MatchString("文件对象不存在", err.Error())
	//			if matchString == true {
	//				return nil
	//			}
	//		}
	//		if resp.StatusCode != 204 {
	//
	//			return fmt.Errorf("DELETE 删除文件时错误", err.Error())
	//		}
	//	}
	//	defer resp.Body.Close()
	return err
}

// 删除目录
func (f *Fs) deleteMune(ctx context.Context, bucket string, directory string) error {

	if directory == "" {
		return nil
	} else {
		fmt.Printf("删除目录 %s \n", directory)
	}
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"
	opts = f.HandlerAccesskeyHeader("DELETE", Path, 3600, nil, nil,
		nil, nil)
	opts.NoResponse = true

	//var response api.Error
	//opts := rest.Opts{
	//	Method:  "DELETE",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion,
	//	Path:    "/dir/" + bucket + "/" + directory + "/",
	//}
	//LOOP:
	//	resp, err := f.srv.Call(ctx, &opts)
	//	if resp == nil {
	//		goto LOOP
	//	} else {
	//		if resp.StatusCode == 404 {
	//			matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
	//			if matchString == true {
	//				return nil
	//			}
	//			matchString, _ = regexp.MatchString("目录不存在", err.Error())
	//			if matchString == true {
	//				return nil
	//			}
	//		}
	//		if resp.StatusCode != 204 {
	//
	//			return fmt.Errorf("couldn't purge directory", err.Error())
	//		}
	//	}
	//	defer resp.Body.Close()
	err := f.pacer.Call(func() (bool, error) {
	LOOP:
		resp, err := f.srv.Call(ctx, &opts)
		if resp == nil {
			goto LOOP
		}
		return f.shouldRetry(ctx, resp, err)
	})

	if err != nil {
		return fmt.Errorf("couldn't purge directory: %w", err)
	}

	return err
}

// 递归删除路径（一个一个目录删除） 正则
func (f *Fs) RegexpDir(ctx context.Context, bucket, bucketPath, basePath string) error {
	if basePath == bucketPath {
		fs.Debugf(f, "目录删除完毕！！")
		return nil
	}
	re := regexp.MustCompile(`[^/]+$`)
	s := regexp.MustCompile("/"+re.FindString(bucketPath)).Split(bucketPath, 2)

	if s[0] == bucketPath {
		err := f.traverseDirectory(ctx, bucket, basePath, basePath)
		if err != nil {
			return err
		}
	}
	err := f.traverseDirectory(ctx, bucket, s[0], basePath)
	if err != nil {
		return err
	}
	return nil

}

// 同步sync 删除空目录
func (f *Fs) syncDeleteEmptyDir(ctx context.Context, bucket, bucketPath, basePath string) error {
	//fmt.Printf("syncDeleteEmptyDir basePath = %s \n", basePath)

	list, err := f.getDirList(ctx, bucket, basePath)
	if err != nil {
		matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
		if matchString {
			return nil
		}
		matchString, _ = regexp.MatchString("目录不存在", err.Error())
		if matchString {
			return nil
		}
		return err
	}
	if list.Count == 0 {
		err := f.deleteMune(ctx, bucket, basePath)
		if err != nil {
			fmt.Printf("错误 %s \n", err)
		}

	}
	for i := range list.Files {

		k := &list.Files[i]
		if k.IsFileOrDir == false {
			basePath = k.AllFilePath

			err := f.syncDeleteEmptyDir(ctx, bucket, bucketPath, basePath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (f *Fs) getDirList(ctx context.Context, bucket, directory string) (traverseDirectory *api.TraverseDirectory, err error) {
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, nil, nil,
		nil, nil)

	//opts := rest.Opts{
	//	Method:  "GET",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//	Path:    bucket + "/" + directory + "/",
	//}
	var response api.TraverseDirectory
	err = f.pacer.Call(func() (bool, error) {
		resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
		return f.shouldRetry(ctx, resp, err)
	})
	if err != nil {
		return nil, err
	}
	return &response, nil
}

// 目录文件遍历
func (f *Fs) v2TraverseDirectory(ctx context.Context, bucket, directory, basePath string) (resps *api.TraverseDirectory, err error) {
	var opts rest.Opts
	Path := "/api/" + defaultVersion + "/dir/" + bucket + "/"
	if directory != "" {
		Path = "/api/" + defaultVersion + "/dir/" + bucket + "/" + directory + "/"

	}
	opts = f.HandlerAccesskeyHeader("GET", Path, 3600, nil, nil,
		nil, nil)

	// directory 为目录
	//opts := rest.Opts{
	//	Method:  "GET",
	//	RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//	Path:    bucket + "/" + directory + "/",
	//}
	//if directory == "" {
	//	opts = rest.Opts{
	//		Method:  "GET",
	//		RootURL: f.opt.Endpoint + "/api/" + defaultVersion + "/dir/",
	//		Path:    bucket + "/",
	//	}
	//}

	var response api.TraverseDirectory
	err = f.pacer.CallNoRetry(func() (bool, error) {
		resp, err := f.srv.CallJSON(ctx, &opts, nil, &response)
		if err != nil {
			matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
			if matchString == true {
				return false, errors.New("NoParentPath")
			}
			return false, err
		}

		return f.shouldRetry(ctx, resp, err)
	})
	if err != nil {
		matchString, _ := regexp.MatchString("目录路径不存在", err.Error())
		if matchString == true {
			return nil, nil
		}
		return nil, err
	}

	return &response, nil

}

// 删除
func (f *Fs) V2DeleteFile(ctx context.Context, resp *api.TraverseDirectory, bucket, directory, basePath string) error {

	for i := range resp.Files {
		file := resp.Files[i]

		// 文件
		if file.IsFileOrDir == true {

			err := f.deleteFile(ctx, bucket, file.AllFilePath, basePath)
			if err != nil {
				return fmt.Errorf("Delete file %s fail, err: %v \n", file.Name, err)
			}

		} else {

			err := f.TargetDeleteFile(ctx, bucket, file.AllFilePath, basePath)
			if err != nil {
				return fmt.Errorf("TraverseDirectory %s fail, err: %v \n", file.Name, err)
			}
		}

	}

	return nil
}

func (f *Fs) TargetDeleteFile(ctx context.Context, bucket, directory, basePath string) error {
	traverseDirectory, err := f.v2TraverseDirectory(ctx, bucket, directory, basePath)
	if err != nil {
		return err
	}
	if traverseDirectory.Count == 0 {
		err := f.deleteMune(ctx, bucket, directory)
		if err != nil {
			return fmt.Errorf("delete mune %v", err)
		}
		return nil
	}

	err = f.V2DeleteFile(ctx, traverseDirectory, bucket, directory, basePath)
	if err != nil {
		return err
	}
	return nil

}

func (f *Fs) RemoveEmptyDirectory(ctx context.Context, bucket, directory, basePath string) error {
	if directory == "" {
		return nil
	}
	traverseDirectory, err := f.v2TraverseDirectory(ctx, bucket, directory, basePath)
	if err != nil {
		return err
	}

	if traverseDirectory.Count == 0 {
		err := f.deleteMune(ctx, bucket, directory)
		if err != nil {
			return err
		}
	}
	return nil
}

////////////////////////////////////////////////////////////
// 最终采用的内容
// ReaderMd5 计算 io.Reader md5 值
func (f *Fs) ReaderMd5(ctx context.Context, buf io.Reader) string {
	md5 := md5.New()
	io.Copy(md5, buf)
	return hex.EncodeToString(md5.Sum(nil))
}

// BufferWirteReader
// @title   BufferWirteReader
// @description   申请缓存存入分片 io.Reader， 只用于v1接口
// @param     k        io.Reader         "文件分片的内容"
// @param     off        int64         "块文件在整个文件中的偏移量"
// @param     chunkS        int64         "文件块的大小"
// @param     name        string         "文件名称"
// @param     o        *Object        "文件对象"
// @return    body 		*bytes.Buffer  "文件块"
// @return	  contenttype 	string		"Content-Type类型"
// @return    error 	error
func (f *Fs) BufferWirteReader(ctx context.Context, k io.Reader, off, chunkS int64, name string, o *Object) (body *bytes.Buffer, contenttype string, error error) {
	bodyBuf := &bytes.Buffer{}
	bodyWrite := multipart.NewWriter(bodyBuf)
	fileWrite, err := bodyWrite.CreateFormFile("chunk", name) // chunk v1接口关键字不能改变
	_, err = io.Copy(fileWrite, k)
	if err != nil {
		fs.Debugf(o, "BufferWirteReader Error : io.Copy : %v", err)
	}
	err = bodyWrite.WriteField("offset", strconv.FormatInt(off, 10))
	if err != nil {
		fs.Debugf(o, "BufferWirteReader Error : bodyWrite.WriteField : %v", err)
	}
	err = bodyWrite.WriteField("chunk_offset", strconv.FormatInt(off, 10))
	if err != nil {
		fs.Debugf(o, "BufferWirteReader Error : bodyWrite.WriteField : %v", err)
	}
	err = bodyWrite.WriteField("chunk_size", strconv.FormatInt(chunkS, 10))
	if err != nil {
		fs.Debugf(o, "BufferWirteReader Error : bodyWrite.WriteField : %v", err)
	}
	contentType := bodyWrite.FormDataContentType()
	bodyWrite.Close() //要关闭，会将w.w.boundary刷写到w.writer中

	return bodyBuf, contentType, err
}
